﻿// Inner Fire 游戏引擎库
// 简单的MESH
//
// Copyright (c) 2024 by 尔西大帝. All rights reserved.
//
// 用于读取简单的模型
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2024-11-19

#pragma once

#include <DirectXCollision.h>
#include <DirectXMath.h>
#include <d3dcompiler.h>
#include <dx/core/dx_core.h>
#include <dx/core/dx_element.h>
#include <memory>
#include <static_string.h>
#include <unordered_map>

namespace ifire::dx {
class RenderItem;
class DxMesh;

/*
 * Mesh类
 */
struct DxSubmesh {
  DxSubmesh() = default;
  DxSubmesh(DxMesh* _parent, ikit::sstr _name, UINT _material_index,
      UINT _start_index_location, UINT _index_count, UINT _base_vertex_location,
      UINT _vertex_count)
      : parent(_parent)
      , name(_name)
      , material_index(_material_index)
      , index_start(_start_index_location)
      , index_count(_index_count)
      , vertex_start(_base_vertex_location)
      , vertex_count(_vertex_count) {};

  DxMesh* parent;
  ikit::sstr name;
  UINT material_index;
  UINT index_start = 0;
  UINT index_count = 0;
  UINT vertex_start = 0;
  UINT vertex_count = 0; // 虽然在渲染上没有用，但是在计算BOX时有用
  DirectX::BoundingBox bounding_box;

  void SetRenderObject(RenderItem* item) const;

  static DxSubmesh EMPTY;
};

struct DxMeshCreateDesc {
  void* pVertexData;       // 顶点的内存地址
  void* pIndexData;        // 索引的内存地址
  size_t VertexSize;       // 拥有多少个顶点
  int VertexStrideSize;    // 每一个顶点的大小
  size_t IndexSize;        // 拥有多少个索引
  int IndexStrideSize;     // 每一个索引的大小
  DXGI_FORMAT IndexFormat; // 索引的格式
};

class DxMesh : DxElement {
public:
  DxMesh(int index, ikit::sstr name);
  DxMesh(const DxMesh& rhs) = delete;
  DxMesh& operator=(const DxMesh& rhs) = delete;
  ~DxMesh() = default;

  // 通过一个DESC创建资源
  void CreateResource(ID3D12Device* device, ID3D12GraphicsCommandList* cmd_list,
      const DxMeshCreateDesc& desc);
  eastl::vector<DxSubmesh>& Submeshs() { return submeshs_; }

  // 几何体的名称
  std::string name;

  // 获取SubMesh
  DxSubmesh& GetSubMesh(ikit::sstr name);
  // 添加一个Submesh
  DxSubmesh& AddSubmesh(ikit::sstr name, UINT material_index,
      UINT start_index_location, UINT index_count, UINT base_vertex_location,
      UINT vertex_count);
  // 获取默认的Submesh（第1个）
  const DxSubmesh& DefaultSubmesh() const;

  // 快速获得
  auto VertexCpuBuffer() const { return vertex_buffer_cpu->GetBufferPointer(); }
  auto IndexCpuBuffer() const { return index_buffer_cpu->GetBufferPointer(); }

  // 顶点内存
  ComPtr<ID3DBlob> vertex_buffer_cpu = nullptr;
  ComPtr<ID3D12Resource> vertex_buffer_gpu = nullptr;
  ComPtr<ID3D12Resource> vertex_buffer_uploader = nullptr;

  // 索引内存
  ComPtr<ID3DBlob> index_buffer_cpu = nullptr;
  ComPtr<ID3D12Resource> index_buffer_gpu = nullptr;
  ComPtr<ID3D12Resource> index_buffer_uploader = nullptr;

  // 与缓冲区相关的数据
  UINT vertex_byte_stride = 0;
  UINT vertex_buffer_byte_size = 0;
  DXGI_FORMAT index_format = DXGI_FORMAT_R16_UINT;
  UINT index_buffer_byte_size = 0;

  // 一个标准结构体下面的子结构体。

  D3D12_VERTEX_BUFFER_VIEW VertexBufferView() const;
  D3D12_INDEX_BUFFER_VIEW IndexBufferView() const;

  // 等待数据上传之后，就可以释放这些内存。
  void DisposeUploaders();

  // 创建一个标准的Quad
  //void CreateStandardQuad(
  //    ID3D12Device* device, ID3D12GraphicsCommandList* cmd_list);

private:
  eastl::vector<DxSubmesh> submeshs_;
};

typedef DxMesh MeshGeometry;
typedef DxSubmesh SubmeshGeometry;

using DxMeshPtr = std::unique_ptr<DxMesh>;


} // namespace ifire::dx